#define _GNU_SOURCE
#include "app_device.h"
#include "app_message.h"
#include "app_pool.h"
#include "app_common.h"
#include "app_mqtt.h"
#include "app_bt.h"
#include "log/log.h"
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> //file contorl
#include <string.h>

static Device *device;
/**
 * 初始化
 */
Device *app_device_init(char *filename)
{
    device = malloc(sizeof(Device));
    device->filename = filename;
    device->fd = open(filename, O_RDWR);
    device->up_buffer = app_buffer_Init(1024);
    device->down_buffer = app_buffer_Init(1024);
    device->read_isRunning = 0; /*线程还没有启动运行*/
    device->last_write_time = 0;
    device->post_read = NULL;
    device->pre_write = NULL;
    
    return device;
}

/**
 * @brief 发送数据到远程服务器的回调函数，在线程池中使用
 *
 * @param arg
 * @return int
 */
static int sendToRemote_task_func(void *arg)
{
    Device *device = (Device *)arg;
    char data_buf[512];
    /*从上行缓冲中读取出一个字符数组消息*/
    int len = app_buffer_readBuffer(device->up_buffer, data_buf, sizeof(data_buf));
    if (len == -1)
    {
        log_error("从上行缓冲区读取数据失败！");
        return -1;
    }
    char *json = app_message_charsToJSON(data_buf, len);

    app_mqtt_send(json, strlen(json));

    free(json);

    return 0;
}

/**
 * @brief 把远程消息数据保存到设备文件的任务(回调函数)，交给线程池去执行
 *
 * @param arg
 * @return int
 */
static int writeToFile_task_func(void *arg)
{
    // Device *device = (Device *)arg;
    char data_buf[128] = {0};

    /*从下行缓冲中读取出一个字符数组消息*/
    int len = app_buffer_readBuffer(device->down_buffer, data_buf, sizeof(data_buf));
    if (len == -1)
    {
        log_error("从下行缓冲区读出数据失败");
        return -1;
    }

    if (len > 0 && device->pre_write)
    {
        /*把字符数组消息转换成蓝牙的数据格式，好让蓝牙芯片向下游设备发送数据*/
        len = device->pre_write(data_buf, strlen(data_buf)); /*返回蓝牙格式的数据的长度*/
        if (len == -1)
        {
            log_error("转换数据失败");
            return -1;
        }
        if (len > 0)
        {
            /*计算两次写入文件的时间差，不足200ms的话，要补足200ms的延时*/
            long long tv = app_common_getTimeStamp();
            if ((tv - device->last_write_time) < 200)
            {
                usleep((200 - (tv - device->last_write_time)) * 1000);
            }
            /*将数据保存到蓝牙对应的串口文件中，，
            不可以使用strlen()判断data_buf长度，因为AT+MESH命令的结束符'\0'会导致计算data_buf长度错误，*/
            ssize_t write_len = write(device->fd, data_buf, len);
            if (write_len != len)
            {
                log_error("数据保存到蓝牙对应的串口文件失败");
                return -1;
            }
            /*记录写入时间的时间戳*/
            device->last_write_time = app_common_getTimeStamp();
            log_debug("写入到文件成功：%.*s", write_len, data_buf);
            return 0;
        }
    }

    return -1;
}

/**
 * @brief 不断读取下游设备的数据
 *
 * @param arg
 * @return void*
 */
void *read_thread_func(void *arg)
{
    while (device->read_isRunning)
    {
        char read_buf[128];
        ssize_t len = read(device->fd, read_buf, sizeof(read_buf));
        if (len == -1)
        {
            log_error("读取文件内容失败");
            return NULL;
        }
        if (len > 0 && device->post_read)
        {
            /*已经获取到了设备数据，把蓝牙数据转换成字符数组数据,返回处理后数据的长度*/
            len = device->post_read(read_buf, len);

            if (len == -1)
            {
                log_error("处理蓝牙数据失败");
                return NULL;
            }
            /*只有有数据，就发到上行缓冲区*/
            if (len > 0)
            {
                log_debug("获取到下游设备的数据：%.*s", len, read_buf);
                /*把字符数组消息保存到上行缓冲区,线程池中的分线程使用任务函数去获取字符数组消息，向mqtt服务器发送*/
                app_buffer_writeBuffer(device->up_buffer, read_buf, len);
                /*把发送数据到远程的函数(任务)交给线程池去执行*/
                app_pool_registerTask(sendToRemote_task_func, device);
            }
        }
    }
    return NULL;
}

/**
 * @brief 接收远程数据的回调函数
 *
 * @param data_buf
 * @param len
 * @return int
 */
int recv_remoteMsg_callback(char *json, int len)
{
    char data_buf[128] = {0};
    int len_rs = app_message_JSONToChars(json, data_buf, sizeof(data_buf));
    if (len_rs == -1)
    {
        log_error("JSONToChars转换失败");
        return -1;
    }
    /*把接收到的数据保存到下行缓冲区*/
    int result = app_buffer_writeBuffer(device->down_buffer, data_buf, len_rs);
    if (result == -1)
    {
        log_error("写入下行缓冲区失败");
        return -1;
    }
    // 把远程消息数据保存到设备文件的任务(回调函数)，交给线程池去执行
    app_pool_registerTask(writeToFile_task_func, NULL);
    return 0;
}

/**
 * 启动设备
 */
int app_device_start(void)
{
    if (device->read_isRunning == 1)
    {
        log_debug("读线程已经在运行中");
        return 0;
    }
    /*启动读线程，并注册读线程的回调函数，用来读取下游设备发向蓝牙芯片的数据*/
    device->read_isRunning = 1;
    pthread_create(&device->read_thread, NULL, read_thread_func, NULL);
    /*注册接收远程数据的回调函数*/
    app_mqtt_registerRecvCallBack(recv_remoteMsg_callback);
    log_debug("设备启动完成");

    return 0;
}

/**
 * 销毁设备
 */
void app_device_destroy(void)
{
    // 释放缓冲区
    app_buffer_Free(device->down_buffer);
    app_buffer_Free(device->up_buffer);
    // 取消读线程
    pthread_cancel(device->read_thread);
    // 关闭设备文件
    close(device->fd);
    // 释放设备
    free(device);
}